#![allow(unused)]
use std::{iter::Peekable, str::Chars};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span(pub usize, pub usize);

#[derive(Clone)]
pub struct Reader<'s> {
	line: usize,
	column: usize,
	offset: usize,
	content: &'s str,
	cursor: Peekable<Chars<'s>>,
}
impl<'s> Iterator for Reader<'s> {
	type Item = char;
	fn next(&mut self) -> Option<char> {
		match self.cursor.next() {
			Some(c) => {
				if c == '\n' {
					self.line += 1;
					self.column = 1;
				} else {
					self.column += 1;
				}
				self.offset += c.len_utf8();
				Some(c)
			}
			_ => None,
		}
	}
}
impl<'s> Reader<'s> {
	#[inline]
	pub fn new(content: &'s str) -> Self {
		Reader {
			line: 1,
			column: 1,
			offset: 0,
			content,
			cursor: content.chars().peekable(),
		}
	}
	#[inline]
	pub fn span(&self) -> Span {
		Span(self.line, self.column)
	}
	#[inline]
	pub fn peek(&mut self) -> Option<char> {
		self.cursor.peek().copied()
	}
	#[inline]
	pub fn rest(&self) -> Option<&'s str> {
		let ret = &self.content[self.offset..];
		if !ret.is_empty() {
			Some(ret)
		} else {
			None
		}
	}
	pub fn cap(&mut self, block: impl Fn(&mut Reader<'s>) -> Result<(), Span>) -> Result<&'s str, Span> {
		let start = self.offset;
		block(self)?;
		let content = self.content[start..self.offset].trim();
		if content.is_empty() {
			Err(self.span())
		} else {
			Ok(content)
		}
	}
	#[inline]
	pub fn eat(&mut self) -> Result<char, Span> {
		match self.next() {
			Some(c) => Ok(c),
			_ => Err(self.span()),
		}
	}
	#[inline]
	pub fn eat_if(&mut self, check: impl Fn(char) -> bool) -> Result<(), Span> {
		match self.peek() {
			Some(c) if check(c) => {
				self.next();
				Ok(())
			}
			_ => Err(self.span()),
		}
	}
	#[inline]
	pub fn eat_while(&mut self, check: impl Fn(char) -> bool) -> Result<(), Span> {
		while self.eat_if(&check).is_ok() {}
		Ok(())
	}
	#[inline]
	pub fn eat_all(&mut self) -> Result<(), Span> {
		self.eat_while(|_| true)
	}
	#[inline]
	pub fn eat_any(&mut self, targets: &str) -> Result<(), Span> {
		self.eat_while(|c| targets.contains(c))
	}
	#[inline]
	pub fn eat_char(&mut self, target: char) -> Result<(), Span> {
		self.eat_if(|c| c == target)
	}
	#[inline]
	pub fn eat_until_any(&mut self, targets: &str) -> Result<(), Span> {
		self.eat_while(|c| !targets.contains(c))
	}
	#[inline]
	pub fn eat_until_char(&mut self, target: char) -> Result<(), Span> {
		self.eat_while(|c| c != target)
	}
}
